
// Copyright (c) WanSheng Intelligent Corp. All rights reserved.
// Licensed under the LGPL v3.0 license

#include "s7-plugin.h"

#define fff FIELD_OFFSET(wa_bus_info_t, tcp_addr)
#pragma message( MACRO_STR(fff) )

s7_device_t *g_s7_devices[MAX_S7_DEVICE] = {0};

wa_plugin_device_info_t *g_devices_info = NULL;
int g_devices_info_size = 0;

int set_device(s7_device_t *device)
{
    int i;
    for (i = 0; i < MAX_S7_DEVICE; i++)
    {
        if (g_s7_devices[i] == NULL)
        {
            g_s7_devices[i] = device;
            return i;
        }
    }

    return -1;
}

s7_device_t *find_device(const char *di)
{
    int i;
    for (i = 0; i < MAX_S7_DEVICE; i++)
    {
        if (g_s7_devices[i] && strcmp(di, g_s7_devices[i]->plugin_cfg->device_name) == 0)
        {
            return g_s7_devices[i];
        }
    }
    return NULL;
}

s7_device_t *find_device_open(const char *di)
{
    s7_device_t *device = find_device(di);
    if (device && device->dc)
    {
        return device;
    }
    return NULL;
}

void open_s7_device(s7_device_t *device)
{
    daveInterface *di;
    daveConnection *dc;
    _daveOSserialType fds = {0};
    if (device->di == NULL)
    {
        if (device->plugin_cfg->bus_info->bus_type == T_Bus_TCP)
        {
            wa_addr_IP_t *addr = &device->plugin_cfg->bus_info->tcp_addr;
            fds.rfd = openSocket(addr->port, addr->ip);
            fds.wfd = fds.rfd;

            if (fds.rfd < 0)
            {
                WARNING("[s7-plugin][%s] Cannot open socket: %s\n", device->plugin_cfg->device_name, strerror(errno));
                return;
            }
        }
        else if (device->plugin_cfg->bus_info->bus_type == T_Bus_Serials)
        {
            char baud_rate[10] = {0};
            wa_addr_serials_t *addr = &device->plugin_cfg->bus_info->serials_addr;
            snprintf(baud_rate, 10, "%d", addr->baud_rate);
            char parity = 'n';
            if(addr->mode) parity = addr->mode[1];
            fds.rfd = setPort(addr->serials_port_name, baud_rate, parity);
            fds.wfd = fds.rfd;
            if (fds.rfd < 0)
            {
                WARNING("[s7-plugin][%s] Cannot open serial: %s\n", device->plugin_cfg->device_name, strerror(errno));
                return;
            }
        }
        else
        {
            return;
        }

        di = daveNewInterface(fds, device->name, device->MPI, device->protocol, device->useSpeed);
        daveSetTimeout(di, 5000000);
        device->di = di;
    }

    if (device->dc == NULL)
    {
        dc = daveNewConnection(device->di, device->mpi_addr, device->rack, device->slot); // insert your rack and slot here
        if(dc)
        {
            int res = daveConnectPLC(dc);
            if (res == 0)
            {
                WARNING("[s7-plugin][%s] PLC has Connected!\n", device->plugin_cfg->device_name);
            }
            else
            {
                WARNING("[s7-plugin][%s] Cannot connect PLC!\n", device->plugin_cfg->device_name);
                return;
            }

            device->dc = dc;
        }
    }

    return;
}

static s7_device_t *parse_customer_config_A(wa_plugin_device_info_t *device_info)
{

    int useProtocol, useSpeed, useSlot, useRack;
    char useParity;

    if (device_info == NULL)
        return NULL;
    if (device_info->custom_cfg == NULL)
        return NULL;
    cJSON *custom_cfg = cJSON_Parse(device_info->custom_cfg);
    if (custom_cfg == NULL)
        return NULL;
    if (!cJSON_IsObject(custom_cfg))
    {
        cJSON_Delete(custom_cfg);
        return NULL;
    }

    const char *protocol = cJSONx_GetObjectString(custom_cfg, "protocol");
    if (strcmp(protocol, "PPI") == 0)
        useProtocol = daveProtoPPI;
    else if (strcmp(protocol, "MPI") == 0)
        useProtocol = daveProtoMPI;
    else if (strcmp(protocol, "MPI2") == 0)
        useProtocol = daveProtoMPI2;
    else if (strcmp(protocol, "MPI3") == 0)
        useProtocol = daveProtoMPI3;
    else if (strcmp(protocol, "MPI4") == 0)
        useProtocol = daveProtoMPI4;
    else if (strcmp(protocol, "ISOTCP") == 0)
        useProtocol = daveProtoISOTCP;
    else if (strcmp(protocol, "ISOTCP243") == 0)
        useProtocol = daveProtoISOTCP243;
    else
    {
        WARNING("[s7-plugin][%s] protocol error!\n", device_info->device_name);
        cJSON_Delete(custom_cfg);
        return NULL;
    }

    if((useProtocol != daveProtoISOTCP && useProtocol != daveProtoISOTCP243)
        && device_info->bus_info->bus_type == T_Bus_TCP)
    {
        WARNING("[s7-plugin][%s] protocol is not TCP but TCP configured!\n", device_info->device_name);
        cJSON_Delete(custom_cfg);
        return NULL;
    }

    const char *name = cJSONx_GetObjectString(custom_cfg, "name");
    if (name == NULL)
        name = "IF1";
    const char *speed = cJSONx_GetObjectString(custom_cfg, "speed");
    if(speed == NULL)
        useSpeed = daveSpeed9k;
    else if (strcmp(speed, "9k") == 0)
        useSpeed = daveSpeed9k;
    else if (strcmp(speed, "19k") == 0)
        useSpeed = daveSpeed19k;
    else if (strcmp(speed, "187k") == 0)
        useSpeed = daveSpeed187k;
    else if (strcmp(speed, "500k") == 0)
        useSpeed = daveSpeed500k;
    else if (strcmp(speed, "1500k") == 0)
        useSpeed = daveSpeed1500k;
    else if (strcmp(speed, "45k") == 0)
        useSpeed = daveSpeed45k;
    else if (strcmp(speed, "93k") == 0)
        useSpeed = daveSpeed93k;
    else
        useSpeed = daveSpeed9k;

    const char * sz_mpi_addr = cJSONx_GetObjectString(custom_cfg, "mpi_addr");
    const char * sz_localMPI = cJSONx_GetObjectString(custom_cfg, "localMPI");
    const char * sz_useRack = cJSONx_GetObjectString(custom_cfg, "rack");
    const char * sz_useSlot = cJSONx_GetObjectString(custom_cfg, "slot");
    if(sz_mpi_addr == NULL || sz_localMPI == NULL || sz_useRack == NULL || sz_useSlot == NULL)
    {
        WARNING("[s7-plugin][%s] mpi_addr or localMPI or rack or slot error!\n", device_info->device_name);
        cJSON_Delete(custom_cfg);
        return NULL;
    }

    int mpi_addr = atoi(sz_mpi_addr);
    int localMPI = atoi(sz_localMPI);
    useSlot = atoi(sz_useSlot);
    useRack = atoi(sz_useRack);

    s7_device_t *s7_device = (s7_device_t *)malloc(sizeof(s7_device_t));
    memset(s7_device, 0, sizeof(s7_device_t));
    s7_device->protocol = useProtocol;
    s7_device->name = strdup(name);
    s7_device->useSpeed = useSpeed;
    s7_device->MPI = localMPI;
    s7_device->mpi_addr = mpi_addr;
    s7_device->rack = useRack;
    s7_device->slot = useSlot;
    s7_device->plugin_cfg = device_info;

    cJSON_Delete(custom_cfg);

    return s7_device;
}

bool load_device_config(const char * plugin_name)
{
    char buf[256];
    int device_num = 0;
    WARNING("Start loading s7 config...");

    assert(g_devices_info == NULL);

    wa_plugin_device_info_t *devices_info = wa_plugin_load_device_info_A(plugin_name, &device_num);

    if (devices_info == NULL)
    {
        WARNING2("load cfg fail");
        return false;
    }

    for (int k = 0; k < device_num; k++)
    {
        wa_plugin_device_info_t *info = &devices_info[k];
        if (find_device(info->device_name))
        {
            WARNING2("device %s already add. skip current.", info->device_name);
            continue;
        }
        printf("bus_info: %p, tcp_addr: %p\n", info->bus_info, &info->bus_info->tcp_addr);
        s7_device_t *s7 = parse_customer_config_A(info);
        if (s7 == NULL)
        {
            continue;
        }

        set_device(s7);
    }

    g_devices_info = devices_info;
    g_devices_info_size = device_num;

    WARNING("Finished loading s7 plugin config\n");
    return true;
}
